﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Asol.Reporting.Support.Data
{
    #region struct ValueData : konvertor pro jakoukoli hodnotu na základní datový typ (ValueDataType)
    /// <summary>
    /// ValueData : konvertor pro jakoukoli hodnotu na základní datový typ (ValueDataType) tam a zpět, detektor typů, podpora převodů a základní operátory
    /// </summary>
    public struct ValueData
    {
        #region Konstrukce
        public override string ToString()
        {
            switch (this._Type)
            {
                case ValueDataType.Null: return "";
                case ValueDataType.Boolean: return this._ValueBoolean.ToString();
                case ValueDataType.Integer: return this._ValueInteger.ToString();
                case ValueDataType.Decimal: return this._ValueDecimal.ToString();
                case ValueDataType.Character: return (this._ValueCharacter == null ? "{null}" : this._ValueCharacter);
                case ValueDataType.DateTime: return this._ValueDateTime.ToString("F", Assistant.DateTimeFormat);
                case ValueDataType.TimeSpan: return this._ValueTimeSpan.ToString("F", Assistant.DateTimeFormat);
            }
            return "???";
        }
        #endregion
        #region Implicit convertors
        /// <summary>Vrátí typovou hodnotu z obecného objektu ValueData</summary><param name="eValue"></param><returns></returns>
        public static implicit operator Boolean(ValueData eValue) { return eValue.ValueBoolean; }
        /// <summary>Vrátí typovou hodnotu z obecného objektu ValueData</summary><param name="eValue"></param><returns></returns>
        public static implicit operator Int32(ValueData eValue) { return (Int32)eValue.ValueInteger; }
        /// <summary>Vrátí typovou hodnotu z obecného objektu ValueData</summary><param name="eValue"></param><returns></returns>
        public static implicit operator Int64(ValueData eValue) { return eValue.ValueInteger; }
        /// <summary>Vrátí typovou hodnotu z obecného objektu ValueData</summary><param name="eValue"></param><returns></returns>
        public static implicit operator Single(ValueData eValue) { return (Single)eValue.ValueDecimal; }
        /// <summary>Vrátí typovou hodnotu z obecného objektu ValueData</summary><param name="eValue"></param><returns></returns>
        public static implicit operator Double(ValueData eValue) { return (Double)eValue.ValueDecimal; }
        /// <summary>Vrátí typovou hodnotu z obecného objektu ValueData</summary><param name="eValue"></param><returns></returns>
        public static implicit operator Decimal(ValueData eValue) { return eValue.ValueDecimal; }
        /// <summary>Vrátí typovou hodnotu z obecného objektu ValueData</summary><param name="eValue"></param><returns></returns>
        public static implicit operator String(ValueData eValue) { return eValue.ValueCharacter; }
        /// <summary>Vrátí typovou hodnotu z obecného objektu ValueData</summary><param name="eValue"></param><returns></returns>
        public static implicit operator Char(ValueData eValue) { return (eValue.ValueCharacter != null && eValue.ValueCharacter.Length > 0 ? eValue.ValueCharacter[0] : (Char)0); }
        /// <summary>Vrátí typovou hodnotu z obecného objektu ValueData</summary><param name="eValue"></param><returns></returns>
        public static implicit operator DateTime(ValueData eValue) { return eValue.ValueDateTime; }
        /// <summary>Vrátí typovou hodnotu z obecného objektu ValueData</summary><param name="eValue"></param><returns></returns>
        public static implicit operator TimeSpan(ValueData eValue) { return eValue.ValueTimeSpan; }
        /// <summary>Vrátí typovou hodnotu z obecného objektu ValueData</summary><param name="eValue"></param><returns></returns>
        public static implicit operator Byte[](ValueData eValue) { return eValue.ValueBinary; }

        /// <summary>Vrátí obecný objekt ValueData z typové hodnotu</summary><param name="value"></param><returns></returns>
        public static implicit operator ValueData(Boolean value) { return new ValueData() { ValueBoolean = value }; }
        /// <summary>Vrátí obecný objekt ValueData z typové hodnotu</summary><param name="value"></param><returns></returns>
        public static implicit operator ValueData(Int32 value) { return new ValueData() { ValueInteger = value }; }
        /// <summary>Vrátí obecný objekt ValueData z typové hodnotu</summary><param name="value"></param><returns></returns>
        public static implicit operator ValueData(Int64 value) { return new ValueData() { ValueInteger = value }; }
        /// <summary>Vrátí obecný objekt ValueData z typové hodnotu</summary><param name="value"></param><returns></returns>
        public static implicit operator ValueData(Decimal value) { return new ValueData() { ValueDecimal = value }; }
        /// <summary>Vrátí obecný objekt ValueData z typové hodnotu</summary><param name="value"></param><returns></returns>
        public static implicit operator ValueData(Single value) { return new ValueData() { ValueDecimal = (Decimal)value }; }
        /// <summary>Vrátí obecný objekt ValueData z typové hodnotu</summary><param name="value"></param><returns></returns>
        public static implicit operator ValueData(Double value) { return new ValueData() { ValueDecimal = (Decimal)value }; }
        /// <summary>Vrátí obecný objekt ValueData z typové hodnotu</summary><param name="value"></param><returns></returns>
        public static implicit operator ValueData(String value) { return new ValueData() { ValueCharacter = value }; }
        /// <summary>Vrátí obecný objekt ValueData z typové hodnotu</summary><param name="value"></param><returns></returns>
        public static implicit operator ValueData(Char value) { return new ValueData() { ValueCharacter = value.ToString() }; }
        /// <summary>Vrátí obecný objekt ValueData z typové hodnotu</summary><param name="value"></param><returns></returns>
        public static implicit operator ValueData(DateTime value) { return new ValueData() { ValueDateTime = (DateTime)value }; }
        /// <summary>Vrátí obecný objekt ValueData z typové hodnotu</summary><param name="value"></param><returns></returns>
        public static implicit operator ValueData(TimeSpan value) { return new ValueData() { ValueTimeSpan = (TimeSpan)value }; }
        /// <summary>Vrátí obecný objekt ValueData z typové hodnotu</summary><param name="value"></param><returns></returns>
        public static implicit operator ValueData(Byte[] value) { return new ValueData() { ValueBinary = value }; }

        /// <summary>Vrátí obecný objekt ValueData z typové hodnotu</summary><param name="value"></param><returns></returns>
        public ValueData (object value)
        {
            this._ValueBoolean = false;
            this._ValueInteger = 0;
            this._ValueDecimal = 0m;
            this._ValueDateTime = DateTime.MinValue;
            this._ValueTimeSpan = TimeSpan.Zero;
            this._ValueCharacter = null;
            this._ValueBinary = null;
            this._ValueOther = null;
            this._Type = ValueDataType.Null;

            this.NativeValue = value;
        }
        /// <summary>
        /// Obsahuje nativní hodnotu
        /// </summary>
        public object NativeValue
        {
            get
            {
                switch (this._Type)
                {
                    case ValueDataType.Null: return null;
                    case ValueDataType.Boolean: return this._ValueBoolean;
                    case ValueDataType.Integer: return this._ValueInteger;
                    case ValueDataType.Decimal: return this._ValueDecimal;
                    case ValueDataType.Character: return this._ValueCharacter;
                    case ValueDataType.DateTime: return this._ValueDateTime;
                    case ValueDataType.TimeSpan: return this._ValueTimeSpan;
                    case ValueDataType.Binary: return this._ValueBinary;
                    case ValueDataType.Other: return this._ValueOther;
                }
                return null;
            }
            set
            {
                this._Type = ValueDataType.Null;
                if (value == null) return;
                if (value is ValueData)
                {   // Pokud vstup je stejného typu jako this, tak do this přeneseme hodnoty z value:
                    CloneContent(((ValueData)value), this);
                    return;
                }
                string typeName = value.GetType().FullName;
                switch (typeName)
                {
                    case "System.Boolean": this.ValueBoolean = (Boolean)value; break;
                    case "System.Byte": this.ValueInteger = (Int64)((Byte)value); break;
                    case "System.SByte": this.ValueInteger = (Int64)((SByte)value); break;
                    case "System.Int16": this.ValueInteger = (Int64)((Int16)value); break;
                    case "System.Int32": this.ValueInteger = (Int64)((Int32)value); break;
                    case "System.Int64": this.ValueInteger = (Int64)((Int64)value); break;
                    case "System.UInt16": this.ValueInteger = (Int64)((UInt16)value); break;
                    case "System.UInt32": this.ValueInteger = (Int64)((UInt32)value); break;
                    case "System.UInt64": this.ValueInteger = (Int64)((UInt64)value); break;
                    case "System.Single": this.ValueDecimal = (Decimal)((Single)value); break;
                    case "System.Double": this.ValueDecimal = (Decimal)((Double)value); break;
                    case "System.Decimal": this.ValueDecimal = (Decimal)((Decimal)value); break;
                    case "System.DateTime": this.ValueDateTime = (DateTime)((DateTime)value); break;
                    case "System.TimeSpan": this.ValueTimeSpan = (TimeSpan)((TimeSpan)value); break;
                    case "System.Char": this.ValueCharacter = (String)((Char)value).ToString(); break;
                    case "System.String": this.ValueCharacter = (String)((String)value); break;
                    case "System.Byte[]": this.ValueBinary = (Byte[])((Byte[])value); break;
                    default: this.ValueOther = value; break;
                }
            }
        }
        /// <summary>
        /// Přenese obsah ze source do target
        /// </summary>
        /// <param name="source"></param>
        /// <param name="target"></param>
        public static void CloneContent(ValueData source, ValueData target)
        {   // Neřeším null, protože this je struct, ne class:
            target._ValueBoolean = source._ValueBoolean;
            target._ValueInteger = source._ValueInteger;
            target._ValueDecimal = source._ValueDecimal;
            target._ValueDateTime = source._ValueDateTime;
            target._ValueTimeSpan = source._ValueTimeSpan;
            target._ValueCharacter = source._ValueCharacter;
            target._ValueBinary = source._ValueBinary;
            target._ValueOther = source._ValueOther;
            target._Type = source._Type;
        }
        #endregion
        #region Operátory
        public static ValueData operator +(ValueData a, ValueData b)
        {
            ValueDataType commonType = _GetCommonType(a, b, _Operation.Add);
            switch (commonType)
            {
                case ValueDataType.Null:
                    return new ValueData() { _Type = ValueDataType.Null };
                case ValueDataType.Boolean:
                    return new ValueData() { ValueBoolean = (a.ValueBoolean || b.ValueBoolean) };
                case ValueDataType.Integer:
                    return new ValueData() { ValueInteger = a.ValueInteger + b.ValueInteger };
                case ValueDataType.Decimal:
                    return new ValueData() { ValueDecimal = a.ValueDecimal + b.ValueDecimal };
                case ValueDataType.DateTime:
                    return new ValueData() { ValueDateTime = a.ValueDateTime.AddDays((double)b.ValueDecimal) };
                case ValueDataType.TimeSpan:
                    return new ValueData() { ValueDateTime = a.ValueDateTime.AddDays((double)b.ValueDecimal) };
                case ValueDataType.Character:
                    return new ValueData() { ValueCharacter = a.ValueCharacter + b.ValueCharacter };
            }
            throw new ValueException("Nelze sloučit dvě hodnoty, kde první je " + a._Type.ToString() + " a druhá " + b._Type.ToString());
        }
        public static ValueData operator -(ValueData a, ValueData b)
        {
            ValueDataType commonType = _GetCommonType(a, b, _Operation.Sub);
            switch (commonType)
            {
                case ValueDataType.Boolean:
                    return new ValueData() { ValueBoolean = (a.ValueBoolean ^ b.ValueBoolean) };
                case ValueDataType.Integer:
                    return new ValueData() { ValueInteger = a.ValueInteger - b.ValueInteger };
                case ValueDataType.Decimal:
                    return new ValueData() { ValueDecimal = a.ValueDecimal - b.ValueDecimal };
                case ValueDataType.DateTime:
                    return new ValueData() { ValueDateTime = a.ValueDateTime.AddDays((double)-b.ValueDecimal) };
            }
            throw new ValueException("Nelze odečíst dvě hodnoty, kde první je " + a._Type.ToString() + " a druhá " + b._Type.ToString());
        }
        public static ValueData operator *(ValueData a, ValueData b)
        {
            ValueDataType commonType = _GetCommonType(a, b, _Operation.Multiply);
            switch (commonType)
            {
                case ValueDataType.Boolean:
                    return new ValueData() { ValueBoolean = (a.ValueBoolean && b.ValueBoolean) };
                case ValueDataType.Integer:
                    return new ValueData() { ValueInteger = a.ValueInteger * b.ValueInteger };
                case ValueDataType.Decimal:
                    return new ValueData() { ValueDecimal = a.ValueDecimal * b.ValueDecimal };
            }
            throw new ValueException("Nelze násobit dvě hodnoty, kde první je " + a._Type.ToString() + " a druhá " + b._Type.ToString());
        }
        public static ValueData operator /(ValueData a, ValueData b)
        {
            ValueDataType commonType = _GetCommonType(a, b, _Operation.Multiply);
            switch (commonType)
            {
                case ValueDataType.Integer:
                    return new ValueData() { ValueInteger = a.ValueInteger / b.ValueInteger };
                case ValueDataType.Decimal:
                    return new ValueData() { ValueDecimal = a.ValueDecimal / b.ValueDecimal };
            }
            throw new ValueException("Nelze dělit dvě hodnoty, kde první je " + a._Type.ToString() + " a druhá " + b._Type.ToString());
        }

        public static ValueData operator ==(ValueData a, ValueData b)
        {
            ValueDataType commonType = _GetCommonType(a, b, _Operation.Compare);
            switch (commonType)
            {
                case ValueDataType.Boolean:
                    return new ValueData() { ValueBoolean = (a.ValueBoolean == b.ValueBoolean) };
                case ValueDataType.Integer:
                    return new ValueData() { ValueBoolean = a.ValueInteger == b.ValueInteger };
                case ValueDataType.Decimal:
                    return new ValueData() { ValueBoolean = a.ValueDecimal == b.ValueDecimal };
                case ValueDataType.DateTime:
                    return new ValueData() { ValueBoolean = a.ValueDateTime == b.ValueDateTime };
                case ValueDataType.Character:
                    return new ValueData() { ValueBoolean = a.ValueCharacter == b.ValueCharacter };
            }
            throw new ValueException("Nelze srovnávat dvě hodnoty, kde první je " + a._Type.ToString() + " a druhá " + b._Type.ToString());
        }
        public static ValueData operator !=(ValueData a, ValueData b)
        {
            ValueDataType commonType = _GetCommonType(a, b, _Operation.Compare);
            switch (commonType)
            {
                case ValueDataType.Boolean:
                    return new ValueData() { ValueBoolean = (a.ValueBoolean != b.ValueBoolean) };
                case ValueDataType.Integer:
                    return new ValueData() { ValueBoolean = a.ValueInteger != b.ValueInteger };
                case ValueDataType.Decimal:
                    return new ValueData() { ValueBoolean = a.ValueDecimal != b.ValueDecimal };
                case ValueDataType.DateTime:
                    return new ValueData() { ValueBoolean = a.ValueDateTime != b.ValueDateTime };
                case ValueDataType.Character:
                    return new ValueData() { ValueBoolean = a.ValueCharacter != b.ValueCharacter };
            }
            throw new ValueException("Nelze srovnávat dvě hodnoty, kde první je " + a._Type.ToString() + " a druhá " + b._Type.ToString());
        }

        public static ValueData operator >(ValueData a, ValueData b)
        {
            ValueDataType commonType = _GetCommonType(a, b, _Operation.Compare);
            switch (commonType)
            {
                case ValueDataType.Integer:
                    return new ValueData() { ValueBoolean = a.ValueInteger > b.ValueInteger };
                case ValueDataType.Decimal:
                    return new ValueData() { ValueBoolean = a.ValueDecimal > b.ValueDecimal };
                case ValueDataType.DateTime:
                    return new ValueData() { ValueBoolean = a.ValueDateTime > b.ValueDateTime };
                case ValueDataType.Character:
                    return new ValueData() { ValueBoolean = a.ValueCharacter.CompareTo(b.ValueCharacter) > 0 };
            }
            throw new ValueException("Nelze určit vztah dvou hodnot, kde první je " + a._Type.ToString() + " a druhá " + b._Type.ToString());
        }
        public static ValueData operator <(ValueData a, ValueData b)
        {
            ValueDataType commonType = _GetCommonType(a, b, _Operation.Compare);
            switch (commonType)
            {
                case ValueDataType.Integer:
                    return new ValueData() { ValueBoolean = a.ValueInteger < b.ValueInteger };
                case ValueDataType.Decimal:
                    return new ValueData() { ValueBoolean = a.ValueDecimal < b.ValueDecimal };
                case ValueDataType.DateTime:
                    return new ValueData() { ValueBoolean = a.ValueDateTime < b.ValueDateTime };
                case ValueDataType.Character:
                    return new ValueData() { ValueBoolean = a.ValueCharacter.CompareTo(b.ValueCharacter) < 0 };
            }
            throw new ValueException("Nelze určit vztah dvou hodnot, kde první je " + a._Type.ToString() + " a druhá " + b._Type.ToString());
        }
        public static ValueData operator >=(ValueData a, ValueData b)
        {
            ValueDataType commonType = _GetCommonType(a, b, _Operation.Compare);
            switch (commonType)
            {
                case ValueDataType.Integer:
                    return new ValueData() { ValueBoolean = a.ValueInteger >= b.ValueInteger };
                case ValueDataType.Decimal:
                    return new ValueData() { ValueBoolean = a.ValueDecimal >= b.ValueDecimal };
                case ValueDataType.DateTime:
                    return new ValueData() { ValueBoolean = a.ValueDateTime >= b.ValueDateTime };
                case ValueDataType.Character:
                    return new ValueData() { ValueBoolean = a.ValueCharacter.CompareTo(b.ValueCharacter) >= 0 };
            }
            throw new ValueException("Nelze určit vztah dvou hodnot, kde první je " + a._Type.ToString() + " a druhá " + b._Type.ToString());
        }
        public static ValueData operator <=(ValueData a, ValueData b)
        {
            ValueDataType commonType = _GetCommonType(a, b, _Operation.Compare);
            switch (commonType)
            {
                case ValueDataType.Integer:
                    return new ValueData() { ValueBoolean = a.ValueInteger <= b.ValueInteger };
                case ValueDataType.Decimal:
                    return new ValueData() { ValueBoolean = a.ValueDecimal <= b.ValueDecimal };
                case ValueDataType.DateTime:
                    return new ValueData() { ValueBoolean = a.ValueDateTime <= b.ValueDateTime };
                case ValueDataType.Character:
                    return new ValueData() { ValueBoolean = a.ValueCharacter.CompareTo(b.ValueCharacter) <= 0 };
            }
            throw new ValueException("Nelze určit vztah dvou hodnot, kde první je " + a._Type.ToString() + " a druhá " + b._Type.ToString());
        }

        /// <summary>
        /// Vrací typ, do něhož lze sloučit hodnotu daných dvou typů (typicky při sčítání)
        /// </summary>
        /// <param name="a"></param>
        /// <param name="b"></param>
        /// <returns></returns>
        private static ValueDataType _GetCommonType(ValueData a, ValueData b, _Operation operation)
        {
            return _GetCommonType(a._Type, b._Type, operation);
        }
        /// <summary>
        /// Vrací typ, do něhož lze sloučit hodnotu daných dvou typů (typicky při sčítání)
        /// </summary>
        /// <param name="a"></param>
        /// <param name="b"></param>
        /// <returns></returns>
        private static ValueDataType _GetCommonType(ValueDataType a, ValueDataType b, _Operation operation)
        {
            // Nezávislé na typu operace:
            if (a == ValueDataType.Decimal && (b == ValueDataType.Integer || b == ValueDataType.Decimal)) return ValueDataType.Decimal;
            if (b == ValueDataType.Decimal && (a == ValueDataType.Integer || a == ValueDataType.Decimal)) return ValueDataType.Decimal;
            if (a == ValueDataType.Integer && b == ValueDataType.Integer) return ValueDataType.Integer;

            // Částečně závislé na typu operace:
            if (a == ValueDataType.Character || b == ValueDataType.Character)
            {
                if (operation == _Operation.Add || operation == _Operation.Compare)
                    return ValueDataType.Character;
                return ValueDataType.Null;
            }

            if (a == ValueDataType.Boolean || b == ValueDataType.Boolean)
            {
                if (operation == _Operation.Add || operation == _Operation.Sub || operation == _Operation.Multiply || operation == _Operation.Compare)
                    return ValueDataType.Boolean;
                return ValueDataType.Null;
            }

            // Závislé na typu operace:
            if (a == ValueDataType.DateTime)
            {
                switch (operation)
                {
                    case _Operation.Add:
                    case _Operation.Sub:
                        if (b == ValueDataType.Integer || b == ValueDataType.Decimal || b == ValueDataType.TimeSpan)
                            return ValueDataType.DateTime;
                        return ValueDataType.Null;
                    case _Operation.Compare:
                        if (b == ValueDataType.DateTime)
                            return ValueDataType.DateTime;
                        return ValueDataType.Null;
                }
                return ValueDataType.Null;
            }

            return ValueDataType.Null;
        }
        private enum _Operation { None, Add, Sub, Multiply, Compare }
        #endregion
        #region Public typové property
        /// <summary>
        /// Prázdná hodnota (null)
        /// </summary>
        public static ValueData Empty { get { return new ValueData(); } }
        /// <summary>
        /// Je hodnota prázdná (typ je null)?
        /// </summary>
        public bool IsEmpty { get { return (this._Type == ValueDataType.Null); } }
        /// <summary>
        /// Hodnota typu Boolean
        /// </summary>
        public Boolean ValueBoolean
        {
            get
            {
                switch (this._Type)
                {
                    case ValueDataType.Null: return false;
                    case ValueDataType.Boolean: return this._ValueBoolean;
                    case ValueDataType.Integer: return (this._ValueInteger != 0);
                    case ValueDataType.Decimal: return (this._ValueDecimal != 0m);
                    case ValueDataType.TimeSpan: return (this._ValueTimeSpan.Ticks != 0L);
                    case ValueDataType.Character:
                        if (String.IsNullOrEmpty(this._ValueCharacter))
                            return false;
                        return ("AaYy1".IndexOf(this._ValueCharacter[0]) >= 0);
                }
                throw new ValueException("Hodnotu typu " + this._Type.ToString() + " nelze konvertovat na Boolean.");
            }
            set
            {
                this._ValueBoolean = value;
                this._Type = ValueDataType.Boolean;
            }
        }
        /// <summary>
        /// Hodnota typu Int64 
        /// </summary>
        public Int64 ValueInteger
        {
            get
            {
                switch (this._Type)
                {
                    case ValueDataType.Null: return 0L;
                    case ValueDataType.Boolean: return (this._ValueBoolean ? (Int64)1 : (Int64)0);
                    case ValueDataType.Integer: return (Int64)this._ValueInteger;
                    case ValueDataType.Decimal: return (Int64)this._ValueDecimal;
                    case ValueDataType.TimeSpan: return (Int64)this._ValueTimeSpan.Days;
                    case ValueDataType.Character:
                        if (String.IsNullOrEmpty(this._ValueCharacter))
                            return 0L;
                        Int64 value;
                        if (Int64.TryParse(this._ValueCharacter, out value))
                            return value;
                        break;
                }
                throw new ValueException("Hodnotu typu " + this._Type.ToString() + " nelze konvertovat na Int64.");
            }
            set
            {
                this._ValueInteger = value;
                this._Type = ValueDataType.Integer;
            }
        }
        /// <summary>
        /// Hodnota typu Int32
        /// </summary>
        public Int32 ValueInteger32
        {
            get
            {
                switch (this._Type)
                {
                    case ValueDataType.Null: return 0;
                    case ValueDataType.Boolean: return (this._ValueBoolean ? (Int32)1 : (Int32)0);
                    case ValueDataType.Integer: return (Int32)this._ValueInteger;
                    case ValueDataType.Decimal: return (Int32)this._ValueDecimal;
                    case ValueDataType.TimeSpan: return (Int32)this._ValueTimeSpan.Days;
                    case ValueDataType.Character:
                        if (String.IsNullOrEmpty(this._ValueCharacter))
                            return 0;
                        Int32 value;
                        if (Int32.TryParse(this._ValueCharacter, out value))
                            return value;
                        break;
                }
                throw new ValueException("Hodnotu typu " + this._Type.ToString() + " nelze konvertovat na Int32.");
            }
            set
            {
                this._ValueInteger = value;
                this._Type = ValueDataType.Integer;
            }
        }
        /// <summary>
        /// Hodnota typu Decimal
        /// </summary>
        public Decimal ValueDecimal
        {
            get
            {
                switch (this._Type)
                {
                    case ValueDataType.Null: return 0m;
                    case ValueDataType.Boolean: return (this._ValueBoolean ? 1m : 0m);
                    case ValueDataType.Integer: return (Decimal)this._ValueInteger;
                    case ValueDataType.Decimal: return this._ValueDecimal;
                    case ValueDataType.TimeSpan: return (Decimal)this._ValueTimeSpan.TotalDays;
                    case ValueDataType.Character:
                        if (String.IsNullOrEmpty(this._ValueCharacter))
                            return 0m;
                        Decimal value;
                        if (Decimal.TryParse(this._ValueCharacter, out value))
                            return value;
                        break;
                }
                throw new ValueException("Hodnotu typu " + this._Type.ToString() + " nelze konvertovat na Decimal.");
            }
            set
            {
                this._ValueDecimal = value;
                this._Type = ValueDataType.Decimal;
            }
        }
        /// <summary>
        /// Hodnota typu DateTime
        /// </summary>
        public DateTime ValueDateTime
        {
            get
            {
                switch (this._Type)
                {
                    case ValueDataType.Null: return DateTime.MinValue;
                    case ValueDataType.DateTime: return this._ValueDateTime;
                    case ValueDataType.Character:
                        if (String.IsNullOrEmpty(this._ValueCharacter))
                            throw new ValueException("Prázdnou hodnotu typu " + this._Type.ToString() + " nelze konvertovat na DateTime.");
                        DateTime value;
                        if (Assistant.ParseText(this._ValueCharacter, out value))
                            return value;
                        throw new ValueException("Textovou hodnotu " + this._ValueCharacter + " nelze konvertovat na DateTime.");
                }
                throw new ValueException("Hodnotu typu " + this._Type.ToString() + " nelze konvertovat na DateTime.");
            }
            set
            {
                this._ValueDateTime = value;
                this._Type = ValueDataType.DateTime;
            }
        }
        /// <summary>
        /// Hodnota typu TimeSpan
        /// </summary>
        public TimeSpan ValueTimeSpan
        {
            get
            {
                switch (this._Type)
                {
                    case ValueDataType.Null: return TimeSpan.Zero;
                    case ValueDataType.Integer: return TimeSpan.FromDays(this._ValueInteger);
                    case ValueDataType.Decimal: return TimeSpan.FromDays((Double)this._ValueDecimal);
                    case ValueDataType.TimeSpan: return this._ValueTimeSpan;
                    case ValueDataType.Character:
                        if (String.IsNullOrEmpty(this._ValueCharacter))
                            throw new ValueException("Prázdnou hodnotu typu " + this._Type.ToString() + " nelze konvertovat na TimeSpan.");
                        TimeSpan value;
                        if (Assistant.ParseText(this._ValueCharacter, out value))
                            return value;
                        throw new ValueException("Textovou hodnotu " + this._ValueTimeSpan + " nelze konvertovat na TimeSpan.");
                }
                throw new ValueException("Hodnotu typu " + this._Type.ToString() + " nelze konvertovat na TimeSpan.");
            }
            set
            {
                this._ValueTimeSpan = value;
                this._Type = ValueDataType.TimeSpan;
            }
        }
        /// <summary>
        /// Hodnota typu String
        /// </summary>
        public String ValueCharacter
        {
            get
            {
                switch (this._Type)
                {
                    case ValueDataType.Null: return null;
                    case ValueDataType.Boolean: return this._ValueBoolean.ToString();
                    case ValueDataType.Integer: return this._ValueInteger.ToString();
                    case ValueDataType.Decimal: return this._ValueDecimal.ToString();
                    case ValueDataType.DateTime: return this._ValueDateTime.ToString("F", Assistant.DateTimeFormat);
                    case ValueDataType.TimeSpan: return this._ValueTimeSpan.ToString("F", Assistant.DateTimeFormat);
                    case ValueDataType.Character: return this._ValueCharacter;
                }
                throw new ValueException("Hodnotu typu " + this._Type.ToString() + " nelze konvertovat na String.");
            }
            set
            {
                this._ValueCharacter = value;
                this._Type = ValueDataType.Character;
            }
        }
        /// <summary>
        /// Hodnota typu Binary
        /// </summary>
        public Byte[] ValueBinary
        {
            get
            {
                switch (this._Type)
                {
                    case ValueDataType.Null: return null;
                    case ValueDataType.Binary: return this._ValueBinary;
                }
                throw new ValueException("Hodnotu typu " + this._Type.ToString() + " nelze konvertovat na Binary.");
            }
            set
            {
                this._ValueBinary = value;
                this._Type = ValueDataType.Binary;
            }
        }
        /// <summary>
        /// Hodnota typu Other
        /// </summary>
        public Object ValueOther
        {
            get
            {
                switch (this._Type)
                {
                    case ValueDataType.Null: return null;
                    case ValueDataType.Other: return this._ValueOther;
                }
                throw new ValueException("Hodnotu typu " + this._Type.ToString() + " nelze konvertovat na Other.");
            }
            set
            {
                this._ValueOther = value;
                this._Type = ValueDataType.Other;
            }
        }
        /// <summary>
        /// Aktuální použitý typ v tomto objektu
        /// </summary>
        public ValueDataType Type { get { return this._Type; } }
        /// <summary>
        /// Výraz do SQL filtru
        /// </summary>
        public string AsSql
        {
            get
            {
                switch (this._Type)
                {
                    case ValueDataType.Null: return "null";
                    case ValueDataType.Boolean: return (this.ValueBoolean ? "1" : "0");
                    case ValueDataType.Integer: return this.ValueInteger.ToString();
                    case ValueDataType.Decimal: return this._DecimalToString(this.ValueDecimal, '.');
                    case ValueDataType.DateTime: return "'" + this.ValueDateTime.ToString("yyyy-MM-dd HH:mm:ss.fff") + "'";
                    case ValueDataType.TimeSpan: return "'" + this.ValueDateTime.ToString("d'; 'HH':'mm':'ss'.'fff") + "'";
                    case ValueDataType.Binary: return "null";
                    case ValueDataType.Character: return (this.ValueCharacter == null ? "null" : "'" + this.ValueCharacter.Replace("'", "''") + "'");
                }
                return "null";
            }
        }
        /// <summary>
        /// Desetinné číslo převede na standardní strojový string
        /// </summary>
        /// <param name="value"></param>
        /// <param name="decimalSeparator"></param>
        /// <returns></returns>
        private string _DecimalToString(decimal value, char decimalSeparator)
        {
            string text = value.ToString("############0.0#####").Trim();
            text = text
                .Replace(',', decimalSeparator)
                .Replace('.', decimalSeparator);
            while (text.Length > 1 && text.Contains(decimalSeparator) && text.EndsWith("0"))
                text = text.Substring(0, text.Length - 1);
            if (text.Length > 1 && text.EndsWith(decimalSeparator.ToString()))
                text = text.Substring(0, text.Length - 1);
            return text;
        }
        /// <summary>
        /// Jsou přesně stejné?
        /// </summary>
        /// <param name="a"></param>
        /// <param name="b"></param>
        /// <returns></returns>
        public static bool ExactEqual(ValueData a, ValueData b)
        {
            if (a.Type != b.Type) return false;
            switch (a.Type)
            {
                case ValueDataType.Null: return true;
                case ValueDataType.Boolean: return (a._ValueBoolean == b._ValueBoolean);
                case ValueDataType.Integer: return (a._ValueInteger == b._ValueInteger);
                case ValueDataType.Decimal: return (a._ValueDecimal == b._ValueDecimal);
                case ValueDataType.DateTime: return (a._ValueDateTime == b._ValueDateTime);
                case ValueDataType.TimeSpan: return (a._ValueTimeSpan == b._ValueTimeSpan);
                case ValueDataType.Binary: return (a._ValueBinary == b._ValueBinary);
                case ValueDataType.Character: return (a._ValueCharacter == b._ValueCharacter);
            }
            return false;
        }
        #endregion
        #region Static služby
        /// <summary>
        /// Vrátí typ dat (ValueDataType) z daného objektu
        /// </summary>
        /// <param name="value"></param>
        /// <returns></returns>
        public static ValueDataType GetValueDataType(object value)
        {
            if (value == null) return ValueDataType.Null;
            
            return GetValueDataTypeFromType(value.GetType());
        }
        /// <summary>
        /// Vrátí typ dat (ValueDataType) z daného objektu
        /// </summary>
        /// <param name="value"></param>
        /// <returns></returns>
        public static ValueDataType GetValueDataType(object value, out ValueData valueData)
        {
            valueData = ValueData.Empty;
            if (value == null) return ValueDataType.Null;
            string nsName = value.GetType().NsName();
            switch (nsName)
            {
                case "System.Boolean": valueData.ValueBoolean = (Boolean)value; return ValueDataType.Integer;
                case "System.Byte": valueData.ValueInteger = (Int64)((Byte)value); return ValueDataType.Integer;
                case "System.SByte": valueData.ValueInteger = (Int64)((SByte)value); return ValueDataType.Integer;
                case "System.Int16": valueData.ValueInteger = (Int64)((Int16)value); return ValueDataType.Integer;
                case "System.Int32": valueData.ValueInteger = (Int64)((Int32)value); return ValueDataType.Integer;
                case "System.Int64": valueData.ValueInteger = (Int64)((Int64)value); return ValueDataType.Integer;
                case "System.UInt16": valueData.ValueInteger = (Int64)((UInt16)value); return ValueDataType.Integer;
                case "System.UInt32": valueData.ValueInteger = (Int64)((UInt32)value); return ValueDataType.Integer;
                case "System.UInt64": valueData.ValueInteger = (Int64)((UInt64)value); return ValueDataType.Integer;
                case "System.Single": valueData.ValueDecimal = (Decimal)((Single)value); return ValueDataType.Decimal;
                case "System.Double": valueData.ValueDecimal = (Decimal)((Double)value); return ValueDataType.Decimal;
                case "System.Decimal": valueData.ValueDecimal = (Decimal)((Decimal)value); return ValueDataType.Decimal;
                case "System.DateTime": valueData.ValueDateTime = (DateTime)((DateTime)value); return ValueDataType.DateTime;
                case "System.TimeSpan": valueData.ValueTimeSpan = (TimeSpan)((TimeSpan)value); return ValueDataType.TimeSpan;
                case "System.Char": valueData.ValueCharacter = (String)((Char)value).ToString(); return ValueDataType.Character;
                case "System.String": valueData.ValueCharacter = (String)((String)value); return ValueDataType.Character;
                case "System.Byte[]": valueData.ValueBinary = (Byte[])((Byte[])value); return ValueDataType.Binary;
                default: valueData.ValueOther = value; return ValueDataType.Other;
            }
        }
        /// <summary>
        /// Vrátí typ dat (ValueDataType) z daného objektu
        /// </summary>
        /// <param name="value"></param>
        /// <returns></returns>
        public static ValueDataType GetValueDataTypeFromType(Type type)
        {
            string nsName = type.NsName();
            switch (nsName)
            {
                case "System.Boolean": return ValueDataType.Integer;
                case "System.Byte": return ValueDataType.Integer;
                case "System.SByte": return ValueDataType.Integer;
                case "System.Int16": return ValueDataType.Integer;
                case "System.Int32": return ValueDataType.Integer;
                case "System.Int64": return ValueDataType.Integer;
                case "System.UInt16": return ValueDataType.Integer;
                case "System.UInt32": return ValueDataType.Integer;
                case "System.UInt64": return ValueDataType.Integer;
                case "System.Single": return ValueDataType.Decimal;
                case "System.Double": return ValueDataType.Decimal;
                case "System.Decimal": return ValueDataType.Decimal;
                case "System.DateTime": return ValueDataType.DateTime;
                case "System.TimeSpan": return ValueDataType.TimeSpan;
                case "System.Char": return ValueDataType.Character;
                case "System.String": return ValueDataType.Character;
                case "System.Byte[]": return ValueDataType.Binary;
            }
            return ValueDataType.Other;
        }
        /// <summary>
        /// Vrátí typ dat podle jejich názvu.
        /// </summary>
        /// <param name="typeName"></param>
        /// <returns></returns>
        public static ValueDataType GetValueDataTypeFromName(string typeName)
        {
            if (String.IsNullOrEmpty(typeName)) return ValueDataType.Null;
            string name = typeName.Trim().ToLower();
            switch (name)
            {
                case "bool": return ValueDataType.Boolean;
                case "boolean": return ValueDataType.Boolean;
                case "bit": return ValueDataType.Boolean;

                case "byte": return ValueDataType.Integer;
                case "int": return ValueDataType.Integer;
                case "integer": return ValueDataType.Integer;
                case "int16": return ValueDataType.Integer;
                case "int32": return ValueDataType.Integer;
                case "int64": return ValueDataType.Integer;
                case "uint16": return ValueDataType.Integer;
                case "uint32": return ValueDataType.Integer;
                case "uint64": return ValueDataType.Integer;
                
                case "single": return ValueDataType.Decimal;
                case "float": return ValueDataType.Decimal;
                case "double": return ValueDataType.Decimal;
                case "decimal": return ValueDataType.Decimal;
                case "numeric": return ValueDataType.Decimal;
                case "money": return ValueDataType.Decimal;

                case "char": return ValueDataType.Character;
                case "string": return ValueDataType.Character;
                case "varchar": return ValueDataType.Character;
                case "nvarchar": return ValueDataType.Character;
                case "text": return ValueDataType.Character;
                case "ntext": return ValueDataType.Character;

                case "date": return ValueDataType.DateTime;
                case "datetime": return ValueDataType.DateTime;
                case "time": return ValueDataType.DateTime;
                case "smalldatetime": return ValueDataType.DateTime;

                case "timespan": return ValueDataType.TimeSpan;

                case "byte[]": return ValueDataType.Binary;
                case "binary": return ValueDataType.Binary;
                case "varbinary": return ValueDataType.Binary;
                case "image": return ValueDataType.Binary;
                case "picture": return ValueDataType.Binary;

            }
            return ValueDataType.Other;
        }
        #endregion
        #region Private proměnné a enum _DataType
        private Boolean _ValueBoolean;
        private Int64 _ValueInteger;
        private Decimal _ValueDecimal;
        private DateTime _ValueDateTime;
        private TimeSpan _ValueTimeSpan;
        private String _ValueCharacter;
        private byte[] _ValueBinary;
        private Object _ValueOther;
        private ValueDataType _Type;
        #endregion
    }
    #endregion
    #region enum ValueDataType : obecný typ hodnoty
    /// <summary>
    /// Základní datové typy, které se mohou vyskytovat v tabulce (z databáze).
    /// S těmito typy pracuje tabulka, a celý reporting systém.
    /// </summary>
    public enum ValueDataType
    {
        /// <summary>Hodnota NULL</summary>
        [DisplayTextInfo("Bez hodnoty")]
        Null = 0,
        /// <summary>Boolean</summary>
        [DisplayTextInfo("Logická hodnota")]
        Boolean,
        /// <summary>Integer (interně se ukládá Int64)</summary>
        [DisplayTextInfo("Celočíselná hodnota")]
        Integer,
        /// <summary>Decimal</summary>
        [DisplayTextInfo("Desetinné číslo")]
        Decimal,
        /// <summary>Řetězcová (char i string jako String)</summary>
        [DisplayTextInfo("Text")]
        Character,
        /// <summary>Datum a čas</summary>
        [DisplayTextInfo("Datum a čas")]
        DateTime,
        /// <summary>Časový interval</summary>
        [DisplayTextInfo("Doba trvání")]
        TimeSpan,
        /// <summary>Byte[]</summary>
        [DisplayTextInfo("Binární data")]
        Binary,
        /// <summary>Jiný typ</summary>
        [DisplayTextInfo("Jiná hodnota")]
        Other
    }
    #endregion
    #region class ValueException : výjimka vyvolaná v procesu kompilace nebo vyhodnocení výrazu
    /// <summary>
    /// ValueException : výjimka vyvolaná v procesu kompilace nebo vyhodnocení výrazu
    /// </summary>
    public class ValueException : DevException
    {
        public ValueException(string message)
            : base(message)
        { }
    }
    #endregion
}
